#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include "mythread.h"

/* Indicates if a thread needs to be rescheduled */
int rescheduleFlag = FALSE;


/**
 * Scheduler function that will reschedule threads based on new priorities
 *
 */
   static int mythread_schedule(void){	
	int numRunning = mythread_lenq(mythread_runq());
	printf("Length of run queue is: %d\n", numRunning);
	
	//if(mythread_lenq(mythread_readyq()) > 0){
		//rescheduleFlag = FALSE;
		
		int suspend = FALSE;
	
		/* Check threads for set reschedule flags and take action (except for the running thread) */
		mythread_queue_t runQueue = *(mythread_runq());
		mythread_queue_t queueNext;
		
		mythread_t runningThread = mythread_self();
		mythread_t currentThread;
		
		while(rescheduleFlag) {
          rescheduleFlag = FALSE;
		
		if (*(mythread_readyq()) == NULL) {
            break; /* nobody to timeslice to -> done */
        }
		
		int i;
		//q = *(mythread_runq()); q != NULL; q = qnext
		for(i= *(mythread_runq());i!=NULL ;runQueue = queueNext){
			/* Go ahead and set next queue pointer */
			queueNext = runQueue->next;
			currentThread = (mythread_t)runQueue->item;
			
			if (currentThread->reschedule) {
                currentThread->reschedule = FALSE;
				
			if (currentThread == runningThread && currentThread->state & MYTHREAD_RUN) {
                    suspend = TRUE;
					 } else if (currentThread->state & MYTHREAD_RUN && *(mythread_readyq()) != NULL) {
                    mythread_block_thread(currentThread, mythread_readyq(), NULL);
					}
			}
			if (queueNext == *(mythread_runq()) || queueNext == runQueue) {
                break;
		}
	}
 }
return suspend;
}

/**
 * mythread_leave_kernel - leave the monolithic threading kernel w/ preemptions
 */
void mythread_leave_kernel(void){
    mythread_t current = mythread_self();
	/* Start simple, just leave the kernel */
	while (rescheduleFlag && mythread_tryenter_kernel()) { /* reenter */
        if (mythread_schedule() && *(mythread_readyq()) != NULL) {
	    current->state &= ~MYTHREAD_RUN;
	    mythread_block(mythread_readyq(), NULL);
        } else {
            mythread_leave_kernel_nonpreemptive(); /* leave kernel */ 
        }
	}
}

/**
 * Handle SIGVTALRM by calling the scheduler when necessary and performing time slicing
 *
 *@param sig  The signal that is being handled
 */
 static void mythread_timer_handle(int sig){
	/* Check the current threads state to be sure that it is running, otherwise return */
	mythread_t signalledThread = mythread_self();
	int suspend;
	
	signalledThread ->reschedule = TRUE;
	rescheduleFlag = TRUE;
	
	if (!mythread_tryenter_kernel()) {
      return; /* someone else will reschedule me */
    }
        
    if (!signalledThread->reschedule) {
      mythread_leave_kernel_nonpreemptive(); /* leave kernel */ 
      return; /* someone else already rescheduled me */
    }
	
	do {
        if (mythread_schedule() && *(mythread_readyq()) != NULL) {
            mythread_block(mythread_readyq(), NULL);
        } else {
            mythread_leave_kernel_nonpreemptive(); /* leave kernel */ 
        }
    } while (rescheduleFlag && mythread_tryenter_kernel()); /* reenter */
}



/**
 * mythread_init_sched - called once at the start of each thread
 * enable virtual timer signals, install signal handler for it and
 * set time slice to 20ms for virtual timer
 */
void mythread_init_sched(void){
	
	/* setitimer() to generate signals at the time-slice intervals */
	struct itimerval new_timer;
		/* Unmask SIGVTALRM for this thread */
	sigset_t new_mask;
    /* Set the handler for SIGVTALRM to be mythread_timer_handle and set the handler to block SIGVTALRM while it is operating */
	struct sigaction new_handler;
	
	sigemptyset(&new_handler.sa_mask);
	sigaddset(&new_handler.sa_mask, SIGVTALRM);
	sigprocmask(SIG_UNBLOCK, &new_mask, NULL);
	
	new_handler.sa_handler = NULL;
	new_handler.sa_sigaction =mythread_timer_handle;
	sigemptyset(&new_mask);
	sigaction(SIGVTALRM, &new_handler, NULL);
	
	
	new_timer.it_interval.tv_sec = 0;
	new_timer.it_interval.tv_usec = 20000;
	new_timer.it_value.tv_sec = 0;
	new_timer.it_value.tv_usec = 20000;
	if(setitimer(ITIMER_VIRTUAL,&new_timer,NULL) == -1){
		printf("Setitimer FAILED on init!\n");
	}
}

/**
 * mythread_exit_sched - called on thread terminiation (exit) by each thread
 * disable all the features activated in mythread_init_sched()
 *
 */
void mythread_exit_sched(void){
	/* Reset the original SIGVTALRM signal handler
	if(sigaction(SIGVTALRM, &old_handler, NULL) == -1){
		printf("Sigaction FAILED on exit!\n");
	}*/
	/* setitimer() to generate signals at the time-slice intervals */
	struct itimerval new_timer;
		/* Unmask SIGVTALRM for this thread */
	sigset_t new_mask;
    /* Set the handler for SIGVTALRM to be mythread_timer_handle and set the handler to block SIGVTALRM while it is operating */
	struct sigaction new_handler;
	
	sigemptyset(&new_handler.sa_mask);
	sigaddset(&new_handler.sa_mask, SIGVTALRM);
	sigprocmask(SIG_BLOCK, &new_mask, NULL);
	
	/* Reset the original ITIMER_VIRTUAL timer
	errno = 0;
	if(setitimer(ITIMER_VIRTUAL, &old_timer, NULL) == -1){
		perror("Setitimer FAILED on exit!\n");
	}*/
}


/**
 * mythread_attr_setschedparam - set the value of scheduling
 * parameters ('sched_priority') in the attribute,
 * to define/change the thread priority, 
 * see /usr/include/linux/sched.h for struct sched_param
 */
int mythread_attr_setschedparam (mythread_attr_t *attr, struct sched_param *param){
	attr->param = *param;
	return 1;
}

/**
 * mythread_attr_getschedparam - return the value of the scheduling
 * parameters ('sched_priority') from the attribute
 */
int mythread_attr_getschedparam (const mythread_attr_t *attr, struct sched_param *param){
	*param = attr->param;
    return attr->param.sched_priority;
}